package org.tagging.service.impl.philippines;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.tagging.dao.philippines.PhilippinesMapper;
import org.tagging.model.TaggingIndonesian;
import org.tagging.model.TaggingPhilippines;
import org.tagging.model.UnknownWordPhilippines;
import org.tagging.service.TaggingService;
import org.tagging.service.impl.other.ClientUserServiceImpl;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class PhilippinesServiceImpl implements TaggingService<TaggingPhilippines> {
    @Resource
    private PhilippinesMapper philippinesMapper;
    @Autowired
    private ClientUserServiceImpl userService;
    @Autowired
    private AbbreviationPhilippinesServiceImpl abbreviationService;
    @Autowired
    private UnknownWordPhilippinesServiceImpl unknownWordService;

    private void updateUsingBy(List<TaggingPhilippines> taggings) {
        TaggingPhilippines tagging = new TaggingPhilippines();
        Integer userId = userService.retrieveCurrentUserId();
        for (TaggingPhilippines t : taggings) {
            tagging.setId(t.getId());
            tagging.setUsingBy(userId);
            updateSelective(tagging);
        }
    }

    @Override
    public List<TaggingPhilippines> list(Integer size) {
        List<TaggingPhilippines> taggingPhilippines = philippinesMapper.listNotUsingAndUpdated(size);
        updateUsingBy(taggingPhilippines);
        return taggingPhilippines;
    }

    @Override
    public void clearUsingBy(Integer tagId) {
        TaggingPhilippines malay = new TaggingPhilippines();
        malay.setId(tagId);
        malay.setUsingBy(-1);
        philippinesMapper.updateSelective(malay);
    }

    @Override
    public void translate(Integer id, String translation) {
//        System.out.println("UNSUPPORTED PHILIPPINES LANGUAGE!");
        throw new UnsupportedOperationException("translate philippines unsupport!");

    }

    @Override
    public List<TaggingPhilippines> listNotTranslate(Integer size) {
//        return null;
        List<TaggingPhilippines> taggingPhilippines = philippinesMapper.listNotUsingAndTranslated(size);
        updateUsingBy(taggingPhilippines);
        return taggingPhilippines;
    }

    @Override
    public List<TaggingPhilippines> listNotAbbreviate(Integer size) {
        List<TaggingPhilippines> taggingPhilippines = philippinesMapper.listNotUsingAndAbbreviated(size);
        updateUsingBy(taggingPhilippines);
        return taggingPhilippines;
    }

    @Override
    public List<TaggingPhilippines> listNotUnknownWord(Integer size) {
        List<TaggingPhilippines> taggingPhilippines = philippinesMapper.listNotUsingAndUnknownWorded(size);
        updateUsingBy(taggingPhilippines);
        return taggingPhilippines;
//        return null;
    }

    @Override
    public void updateSelective(TaggingPhilippines tagging) {
        tagging.setUpdateTime(LocalDateTime.now());
        philippinesMapper.updateSelective(tagging);
    }

    @Override
    public void updateUpdateByAndTranslated(Integer id) {
        Integer userId = userService.retrieveCurrentUserId();
        TaggingPhilippines t = new TaggingPhilippines();
        t.setId(id);
        t.setUpdateBy(userId);
        t.setUpdateByTranslation(userId);
        updateSelective(t);
    }

    @Override
    public void updateUpdateByAndAbbreviation(Integer tagId) {
        Integer userId = userService.retrieveCurrentUserId();
        TaggingPhilippines t = new TaggingPhilippines();
        t.setUpdateBy(userId);
        t.setId(tagId);
        t.setUpdateByAbbreviation(userId);
        updateSelective(t);
    }

    @Override
    public void updateUpdateByAndUnknownWord(Integer tagId) {
        Integer userId = userService.retrieveCurrentUserId();
        TaggingPhilippines t = new TaggingPhilippines();
        t.setUpdateBy(userId);
        t.setId(tagId);
        t.setUpdateByUnknownWord(userId);
        updateSelective(t);
    }

    private static List<String> abbreviationList;
    private static List<String> abbreviationCompleteList;
    private static List<String> abbreviationCompleteChineseList;
    private static List<String> abbreviationCompleteManualList;
    private static List<String> unknownWordList;
    private static List<String> unknownWordCategoryList;
    private static List<String> unknownWordRemarkList;
    private static List<String> unknownWordCorrectList;
    private static Set<String> skipFields;

    static {
        abbreviationList = new LinkedList<>();
        abbreviationCompleteList = new LinkedList<>();
        abbreviationCompleteChineseList = new LinkedList<>();
        abbreviationCompleteManualList = new LinkedList<>();
        unknownWordList = new LinkedList<>();
        unknownWordCategoryList = new LinkedList<>();
        unknownWordRemarkList = new LinkedList<>();
        unknownWordCorrectList = new LinkedList<>();
        skipFields = new HashSet<>();
        skipFields.add("usingBy");
        skipFields.add("updateBy");
        skipFields.add("updateByTranslation");
        skipFields.add("updateByUnknownWord");
        skipFields.add("updateByAbbreviation");
    }

    @Override
    public List<TaggingPhilippines> queryByTimePeriod(LocalDateTime startTime, LocalDateTime endTime, int mode) {
        return philippinesMapper.queryByTimePeriod(startTime, endTime, mode);
    }

    @Override
    public Workbook constructWorkbookByTimePeriod(LocalDateTime startTime, LocalDateTime endTime, int mode) {
        List<TaggingPhilippines> taggingPhilippines = queryByTimePeriod(startTime, endTime, mode);
        List<UnknownWordPhilippines> unknownWordss = unknownWordService.queryByTimePeriod(startTime, endTime, mode);
//        List<TaggingPhilippines> abbreviationIndonesians = abbreviationService.queryByTimePeriod(startTime, endTime, mode);
        Workbook workbook = new HSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        Row row = sheet.createRow(0);

        Map<String, Integer> map = preConstruct(TaggingIndonesian.class, skipFields);
        preConstruct(row, map);
        int i = 1;
        for (TaggingPhilippines t : taggingPhilippines) {
            Row r = sheet.createRow(i++);
            map.forEach((fieldName, index) -> {
                try {
                    Object valueInvoke = valueInvoke(t, fieldName);
                    r.createCell(index).setCellValue(valueInvoke!=null?String.valueOf(valueInvoke):"");
                } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            //处理unknownWords
//            List<UnknownWordPhilippines> unknownWordIndonesians = unknownWordService.queryByTagId(t.getId());
            List<UnknownWordPhilippines> uwis = searchUnknownWordsByTagId(unknownWordss, t.getId());

            uwis.forEach(each -> {
                unknownWordList.add(each.getUnknownWord() == null ? "" : each.getUnknownWord());
                unknownWordCategoryList.add(each.getCategory() == null ? "" : each.getCategory());
                unknownWordRemarkList.add(each.getRemark() == null ? "" : each.getRemark());
                unknownWordCorrectList.add(each.getUnknownWordCorrect() == null ? "" : each.getUnknownWordCorrect());
            });
            String unknownWords = Arrays.toString(unknownWordList.toArray());
            r.createCell(map.size() + 1).setCellValue(unknownWords);
            String unknownWordCategories = Arrays.toString(unknownWordCategoryList.toArray());
            r.createCell(map.size() + 2).setCellValue(unknownWordCategories);
            String unknownWordRemarks = Arrays.toString(unknownWordRemarkList.toArray());
            r.createCell(map.size() + 3).setCellValue(unknownWordRemarks);
            String unknownWordCorrects = Arrays.toString(unknownWordCorrectList.toArray());
            r.createCell(map.size() + 4).setCellValue(unknownWordCorrects);
            unknownWordList.clear();
            unknownWordCategoryList.clear();
            unknownWordCorrectList.clear();
            unknownWordRemarkList.clear();
        }
        return workbook;
    }

    /**
     * 此方法可抽取为公有方法
     *
     * @param unknownWords
     * @param tagId
     * @return
     */
    private List<UnknownWordPhilippines> searchUnknownWordsByTagId(List<UnknownWordPhilippines> unknownWords, Integer tagId) {
        if (unknownWords.size() == 0) {
            return new ArrayList<>(0);
        }
        int left = 0;
        int right = unknownWords.size() - 1;
        int target = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (tagId.equals(unknownWords.get(mid).getTagId())) {
                target = mid;
                break;
//                return mid;
            } else if (tagId.compareTo(unknownWords.get(mid).getTagId()) > 0) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        if (target == -1) {
            return new ArrayList<>(0);
        }
        //若能找到这个坐标，向其左右延申并记录下来其坐标
        List<UnknownWordPhilippines> targets = new LinkedList<>();
        targets.add(unknownWords.get(target));
        int t1 = target;
        while ((t1 = t1 - 1) > 0 && unknownWords.get(t1).getTagId().equals(unknownWords.get(target).getTagId())) {
            targets.add(unknownWords.get(t1));
        }
        int t2 = target;
        while ((t2 = t2 + 1) < unknownWords.size() && unknownWords.get(t2).getTagId().equals(unknownWords.get(target).getTagId())) {
            targets.add(unknownWords.get(t2));
        }
        return targets;
    }


    private Object valueInvoke(TaggingPhilippines t, String fieldName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = t.getClass().getMethod("get" + toCapital(fieldName));
        return method.invoke(t);
    }

    private void preConstruct(Row r, Map<String, Integer> map) {
        map.forEach((field, index) -> {
            r.createCell(index).setCellValue(field);
        });
        r.createCell(map.size() + 1).setCellValue("unknownWords");
        r.createCell(map.size() + 2).setCellValue("unknownWordCategories");
        r.createCell(map.size() + 3).setCellValue("unknownWordRemarks");
        r.createCell(map.size() + 4).setCellValue("unknownWordCorrects");
    }

}
